#include "ConvertingMotionHandlerDialog.h"
#include "ui_ConvertingMotionHandlerDialog.h"

#include <MMM/MathTools.h>
#include <boost/lexical_cast.hpp>
#include <QMessageBox>

static std::exception_ptr teptr = nullptr;
static MMM::MotionPtr convertedMotion = nullptr;

ConvertingMotionHandlerDialog::ConvertingMotionHandlerDialog(QWidget* parent, MMM::MotionConverterPtr motionConverter, MMM::MotionPtr motion) :
    QDialog(parent),
    ui(new Ui::ConvertingMotionHandlerDialog),
    motionConverter(motionConverter),
    convertingThread(&ConvertingMotionHandlerDialog::convert, motionConverter),
    minTimestep(motion->getMinTimestep()),
    maxTimestep(motion->getMaxTimestep()),
    scaling(100),
    updateInterval(1.0f)
{
    teptr = nullptr;
    convertedMotion = nullptr;

    ui->setupUi(this);

    connect(ui->CancelButton, SIGNAL(clicked()), this, SLOT(cancel()));

    ui->ProgressBar->setMinimum(minTimestep * scaling);
    ui->ProgressBar->setMaximum(maxTimestep * scaling);

    updateTimer = new SoTimerSensor(updateTimerCallback, this);
    updateTimer->setInterval(SbTime(updateInterval));
    startingTime = SbTime::getTimeOfDay();
    updateTimer->schedule();

}

ConvertingMotionHandlerDialog::~ConvertingMotionHandlerDialog()
{
    delete ui;
}

void ConvertingMotionHandlerDialog::convert(MMM::MotionConverterPtr motionConverter)
{
    try
    {
        convertedMotion = motionConverter->convertMotion();
    }
    catch(std::exception& e)
    {
        teptr = std::current_exception();
    }
}

void ConvertingMotionHandlerDialog::updateTimerCallback(void* data, SoSensor* sensor)
{
    ConvertingMotionHandlerDialog* dialog = static_cast<ConvertingMotionHandlerDialog*>(data);
    dialog->update();
}

void ConvertingMotionHandlerDialog::update() {
    if (teptr) {
        try {
           std::rethrow_exception(teptr);
        }
        catch (std::exception& e) {
           std::string message = "Caught exception: " + std::string(e.what());
           QMessageBox* msgBox = new QMessageBox(this);
           msgBox->setText(QString::fromStdString(message));
           MMM_ERROR << message << std::endl;
           msgBox->exec();
        }

        updateTimer->unschedule();
        this->close();
    }

    float currentTimestep = motionConverter->getCurrentTimestep();

    if (currentTimestep < 0.0) {
        ui->TimeLabel->setText(QString::fromStdString("No time information available for current conversion!"));
    }
    else {
        ui->ProgressBar->setValue(currentTimestep * scaling);

        float timespan = (SbTime::getTimeOfDay() - startingTime).getValue();
        float percent = (currentTimestep - minTimestep) / (maxTimestep - minTimestep);
        if (percent > 0) {
            float remainingTime = MMM::Math::roundf(timespan * (1.0f / percent) * (1.0f - percent), 0);
            ui->TimeLabel->setText(QString::fromStdString("Remaining time: " + boost::lexical_cast<std::string>(remainingTime) + " seconds"));
        }
    }

    if (convertedMotion) {
        updateTimer->unschedule();
        this->close();
        emit motionConverted(convertedMotion);
    }
}

void ConvertingMotionHandlerDialog::cancel() {
    motionConverter->cancel();
}
